import base64
from typing import Dict, List, Tuple

import cv2
import gradio as gr
import numpy as np
import requests

# 使用相对导入
from configs.global_config import global_config

# 确保配置已经初始化
if not hasattr(global_config, "raw_config"):
    global_config.initialize()

# 配置API端点
API_BASE_URL = f"http://{global_config.get('server.host', 'localhost')}:{global_config.get('server.port', 5555)}"


def check_api_status() -> bool:
    """检查API服务状态"""
    try:
        response = requests.post(
            f"{API_BASE_URL}/extract_face_features",
            json={"image_base64": "test"},
            timeout=3,
        )
        # 400表示服务存在但参数错误，405表示方法不允许
        return response.status_code in (400, 405)
    except:  # noqa: E722
        return False


def get_image_base64(image: np.ndarray) -> str:
    """将图像转换为base64"""
    if image is None:
        return ""
    _, buffer = cv2.imencode(".jpg", image)
    return base64.b64encode(buffer).decode("utf-8")


def extract_face_features(image: np.ndarray) -> Dict:
    """从图像中提取人脸特征"""
    if image is None:
        return {"error": "没有提供图片"}

    try:
        img_base64 = get_image_base64(image)

        response = requests.post(
            f"{API_BASE_URL}/extract_face_features",
            json={"image_base64": img_base64},
            timeout=30,
        )

        result = response.json()
        if response.status_code != 200:
            return result

        return result
    except Exception as e:
        return {"error": f"处理失败: {str(e)}"}


def match_face_features(
    source_feature: List[float],
    target_feature: List[float],
    similarity_threshold: float = 0.7,
) -> Dict:
    """匹配两组人脸特征"""
    try:
        response = requests.post(
            f"{API_BASE_URL}/match_face_features",
            json={
                "source_feature": source_feature,
                "target_feature": target_feature,
                "similarity_threshold": similarity_threshold,
            },
            timeout=10,
        )

        result = response.json()
        if response.status_code != 200:
            return result

        return result
    except Exception as e:
        return {"error": f"匹配失败: {str(e)}"}


def match_faces(
    source_image: np.ndarray,
    target_image: np.ndarray,
    similarity_threshold: float = 0.7,
) -> Tuple[Dict, np.ndarray, np.ndarray]:
    """匹配两张图片中的人脸"""
    if source_image is None or target_image is None:
        return {"error": "没有提供完整的图片"}, None, None

    try:
        # 提取源图像人脸特征
        source_result = extract_face_features(source_image)
        if "error" in source_result:
            return source_result, None, None

        source_faces = source_result.get("data", [])
        if not source_faces:
            return {"error": "源图像未检测到人脸"}, None, None

        # 我们只使用第一个检测到的人脸作为源
        source_face = source_faces[0]
        source_embedding = source_face.get("embedding", [])
        source_bbox = source_face.get("bbox", [])

        # 提取目标图像人脸特征
        target_result = extract_face_features(target_image)
        if "error" in target_result:
            return target_result, None, None

        target_faces = target_result.get("data", [])
        if not target_faces:
            return {"error": "目标图像未检测到人脸"}, None, None

        # 准备输出图像
        source_output = source_image.copy()
        target_output = target_image.copy()

        # 在源图像上标记出检测到的人脸
        if len(source_bbox) == 4:
            cv2.rectangle(
                source_output,
                (int(source_bbox[0]), int(source_bbox[1])),
                (int(source_bbox[2]), int(source_bbox[3])),
                (0, 255, 255),  # 黄色框，表示这是源人脸
                3,
            )

        # 匹配结果
        match_results = []

        # 依次匹配每个目标人脸
        for idx, target_face in enumerate(target_faces):
            target_embedding = target_face.get("embedding", [])
            target_bbox = target_face.get("bbox", [])

            # 匹配人脸特征
            match_result = match_face_features(
                source_embedding, target_embedding, similarity_threshold
            )

            # 记录匹配结果
            match_info = {"face_id": idx, "bbox": target_bbox, "result": match_result}
            match_results.append(match_info)

            # 在目标图像上标记匹配结果
            if len(target_bbox) == 4:
                matched = match_result.get("data", {}).get("matched", False)
                similarity = match_result.get("data", {}).get("similarity", 0)

                # 红色表示匹配成功，绿色表示匹配失败
                color = (255, 0, 0) if matched else (0, 255, 0)

                # 画出边界框
                cv2.rectangle(
                    target_output,
                    (int(target_bbox[0]), int(target_bbox[1])),
                    (int(target_bbox[2]), int(target_bbox[3])),
                    color,
                    3,
                )

                # 在边界框上方添加相似度文本
                text = f"{similarity * 100:.1f}%"
                cv2.putText(
                    target_output,
                    text,
                    (int(target_bbox[0]), int(target_bbox[1]) - 10),
                    cv2.FONT_HERSHEY_SIMPLEX,
                    0.5,
                    color,
                    3,
                )

        # 汇总结果
        result = {
            "code": 200,
            "message": f"共匹配 {len(target_faces)} 个人脸，相似度阈值: {similarity_threshold * 100}%",
            "data": {
                "source_face": source_face,
                "match_results": match_results,
                "total_faces": len(target_faces),
            },
        }

        return result, source_output, target_output

    except Exception as e:
        return {"error": f"匹配处理失败: {str(e)}"}, None, None


def match_faces_step_by_step(
    source_image: np.ndarray,
    target_image: np.ndarray,
    similarity_threshold: float = 0.7,
):
    """逐步匹配人脸，每个边界框停留0.5秒"""
    if source_image is None or target_image is None:
        return {"error": "没有提供完整的图片"}, None, None, gr.update(visible=True)

    try:
        # 提取源图像人脸特征
        source_result = extract_face_features(source_image)
        if "error" in source_result:
            return source_result, None, None, gr.update(visible=True)

        source_faces = source_result.get("data", [])
        if not source_faces:
            return {"error": "源图像未检测到人脸"}, None, None, gr.update(visible=True)

        # 我们只使用第一个检测到的人脸作为源
        source_face = source_faces[0]
        source_embedding = source_face.get("embedding", [])
        source_bbox = source_face.get("bbox", [])

        # 提取目标图像人脸特征
        target_result = extract_face_features(target_image)
        if "error" in target_result:
            return target_result, None, None, gr.update(visible=True)

        target_faces = target_result.get("data", [])
        if not target_faces:
            return (
                {"error": "目标图像未检测到人脸"},
                None,
                None,
                gr.update(visible=True),
            )

        # 准备输出图像
        source_output = source_image.copy()

        # 在源图像上标记出检测到的人脸
        if len(source_bbox) == 4:
            cv2.rectangle(
                source_output,
                (int(source_bbox[0]), int(source_bbox[1])),
                (int(source_bbox[2]), int(source_bbox[3])),
                (255, 255, 0),  # 黄色框，表示这是源人脸
                3,
            )

        # 第一步：只显示源图像的处理结果
        initial_result = {
            "code": 200,
            "message": "开始匹配...",
            "data": {
                "source_face": source_face,
                "progress": "0/" + str(len(target_faces)),
            },
        }

        yield (
            initial_result,
            source_output,
            target_image.copy(),
            gr.update(visible=False),
        )

        # 匹配结果
        match_results = []

        # 依次匹配每个目标人脸
        for idx, target_face in enumerate(target_faces):
            # 创建新的目标图像副本，只显示当前处理的人脸
            current_target_output = target_image.copy()

            target_embedding = target_face.get("embedding", [])
            target_bbox = target_face.get("bbox", [])

            # 匹配人脸特征
            match_result = match_face_features(
                source_embedding, target_embedding, similarity_threshold
            )

            # 记录匹配结果
            match_info = {"face_id": idx, "bbox": target_bbox, "result": match_result}
            match_results.append(match_info)

            # 在目标图像上标记匹配结果
            if len(target_bbox) == 4:
                matched = match_result.get("data", {}).get("matched", False)
                similarity = match_result.get("data", {}).get("similarity", 0)

                # 红色表示匹配成功，绿色表示匹配失败
                color = (255, 0, 0) if matched else (0, 255, 0)

                # 画出边界框
                cv2.rectangle(
                    current_target_output,
                    (int(target_bbox[0]), int(target_bbox[1])),
                    (int(target_bbox[2]), int(target_bbox[3])),
                    color,
                    3,
                )

                # 在边界框上方添加相似度文本
                text = f"{similarity * 100:.1f}%"
                cv2.putText(
                    current_target_output,
                    text,
                    (int(target_bbox[0]), int(target_bbox[1]) - 10),
                    cv2.FONT_HERSHEY_SIMPLEX,
                    0.5,
                    color,
                    3,
                )

            # 更新进度信息
            progress_result = {
                "code": 200,
                "message": f"正在匹配第 {idx + 1}/{len(target_faces)} 个人脸...",
                "data": {
                    "source_face": source_face,
                    "match_results": match_results[: idx + 1],  # 只包含已处理的结果
                    "progress": f"{idx + 1}/{len(target_faces)}",
                },
            }

            # 每处理一个人脸，就更新一次UI
            yield (
                progress_result,
                source_output,
                current_target_output,
                gr.update(visible=False),
            )

            # 暂停0.5秒
            import time

            time.sleep(0.1)

        # 最终结果：显示所有匹配结果
        final_target_output = target_image.copy()

        # 在最终结果图上绘制所有匹配结果
        for match_info in match_results:
            target_bbox = match_info.get("bbox", [])
            match_result = match_info.get("result", {})

            if len(target_bbox) == 4:
                matched = match_result.get("data", {}).get("matched", False)
                similarity = match_result.get("data", {}).get("similarity", 0)

                # 红色表示匹配成功，绿色表示匹配失败
                color = (255, 0, 0) if matched else (0, 255, 0)

                # 画出边界框
                cv2.rectangle(
                    final_target_output,
                    (int(target_bbox[0]), int(target_bbox[1])),
                    (int(target_bbox[2]), int(target_bbox[3])),
                    color,
                    3,
                )

                # 在边界框上方添加相似度文本
                text = f"{similarity * 100:.1f}%"
                cv2.putText(
                    final_target_output,
                    text,
                    (int(target_bbox[0]), int(target_bbox[1]) - 10),
                    cv2.FONT_HERSHEY_SIMPLEX,
                    0.5,
                    color,
                    3,
                )

        # 汇总结果
        final_result = {
            "code": 200,
            "message": f"匹配完成！共匹配 {len(target_faces)} 个人脸，相似度阈值: {similarity_threshold * 100}%",
            "data": {
                "source_face": source_face,
                "match_results": match_results,
                "total_faces": len(target_faces),
                "completed": True,
            },
        }

        yield final_result, source_output, final_target_output, gr.update(visible=True)

    except Exception as e:
        return {"error": f"匹配处理失败: {str(e)}"}, None, None, gr.update(visible=True)


def create_face_match_ui():
    """创建人脸匹配UI"""
    with gr.Blocks() as face_match_block:
        # API状态提示
        gr.HTML(
            value=f'<div style="color: {"green" if check_api_status() else "red"}">'
            f"API服务状态: {'可用' if check_api_status() else '不可用'}</div>"
        )

        with gr.Row():
            # 左侧：源图像（单人脸）
            with gr.Column():
                gr.Markdown("## 源图像（单个人脸）")
                source_image = gr.Image(label="上传源图像", type="numpy")

            # 右侧：目标图像（多人脸）
            with gr.Column():
                gr.Markdown("## 目标图像（多个人脸）")
                target_image = gr.Image(label="上传目标图像", type="numpy")

        # 参数设置
        with gr.Row():
            similarity_threshold = gr.Slider(
                minimum=0.1,
                maximum=1.0,
                value=0.7,
                step=0.05,
                label="相似度阈值",
                info="值越大匹配越严格",
            )
            match_btn = gr.Button("开始匹配", variant="primary")
            clear_btn = gr.Button("清空")

        # 结果显示
        with gr.Row():
            # 左侧：处理后的源图像
            with gr.Column():
                source_output = gr.Image(label="源图像处理结果", interactive=False)

            # 右侧：处理后的目标图像
            with gr.Column():
                target_output = gr.Image(label="目标图像匹配结果", interactive=False)

        # 匹配详情
        match_details = gr.JSON(label="匹配详情")

        with gr.Accordion("使用说明", open=False):
            gr.Markdown("""
            ### 人脸匹配功能使用说明
            
            1. **左侧上传源图像**：应包含一个清晰的人脸，系统将自动识别第一个检测到的人脸
            2. **右侧上传目标图像**：可以包含多个人脸，系统会将源人脸与每个目标人脸进行匹配
            3. **调整相似度阈值**：值越高要求匹配越严格，默认为0.7
            4. **查看匹配结果**：
               - 黄色框：源图像中检测到的人脸
               - 红色框：匹配成功的目标人脸
               - 绿色框：匹配失败的目标人脸
               - 每个目标人脸边框上方显示相似度百分比
            """)

        # 添加进度显示
        progress_text = gr.HTML(
            value="<div style='text-align: center; font-weight: bold;'>准备就绪</div>",
            visible=False,
        )

        # 事件处理
        match_btn.click(
            fn=match_faces_step_by_step,
            inputs=[source_image, target_image, similarity_threshold],
            outputs=[match_details, source_output, target_output, progress_text],
        )

        clear_btn.click(
            fn=lambda: [None, None, None],
            outputs=[match_details, source_output, target_output],
        )

        return face_match_block


if __name__ == "__main__":
    demo = gr.Blocks(title="人脸匹配测试")
    with demo:
        create_face_match_ui()
    demo.launch()
